/* 
 * File:   Game.cpp
 * Author: Pavel Homolka
 * 
 * Created on 2. Å™Ã­jen 2011, 17:44
 */

#include <iostream>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <fstream>
#include "Game.h"

#define CHECK_MSG_AMOUNT 10
//#define DEBUG 1


using namespace std;

Game::Game() {
}

Game::~Game() {
    //    delete[] discs;
    //    for(int i=0;i<this->numberOfTowers;i++) {
    //        delete[] this->towers.at(i);
    //    }
    //    this->towers.clear();
    //    this->actualMoves.clear();
    //    this->moves.clear();
    //    delete[] this;
}

Game::Game(int numberOfDisks, int numberOfTowers, int targetTower) {
    this->numberOfDisks = numberOfDisks;
    this->numberOfTowers = numberOfTowers;
    this->targetTower = targetTower;
    this->discs = new int[numberOfDisks];
    this->discs[0] = 0;
    this->done = 0;
}

//neni hotova

void Game::newGame() {
    //Nahodne generuje rozmisteni disku na veze a pri tom nepripusti na zacatku prazdnou vez         
    srand(time(NULL));
    do {
        for (int i = this->numberOfDisks - 1; i >= 0; i--) {
            discs[i] = rand() % this->numberOfTowers + 1;
        }
    } while (this->create(this->discs, &towers) != true);

}

void Game::newGame(int* array) {
    //Nahodne generuje rozmisteni disku na veze a pri tom nepripusti na zacatku prazdnou vez         
    //    srand ( time(NULL) );
    //    do {
    //        for(int i=this->numberOfDisks-1;i>=0;i--) {
    //            discs[i] = rand() % this->numberOfTowers + 1;
    //        }  
    //    }
    this->discs = array;
    while (this->create(this->discs, &towers) != true);

}

void Game::printGame() {
    //Vola pro vsechny veze jejich metodu print
    for (int i = 0; i<this->numberOfTowers; i++) {
        this->towers.at(i)->print();
    }
}

void Game::printGame(ostream& out) {
    //Vola pro vsechny veze jejich metodu print
    for (int i = 0; i<this->numberOfTowers; i++) {
        this->towers.at(i)->print(out);
    }


}

bool Game::create(int pole[], vector<Tower*>* out) {
    vector<int> t;
    Tower *tower;
    out->clear();
    t.clear();
    //    for(int i=2;i>0;i--)
    //        t.push_back(i);
    //    tower = new Tower(t,1);
    //    out->push_back(tower);   
    //    t.clear();
    //    for(int i=12;i>3;i--)
    //        t.push_back(i);
    //    tower = new Tower(t,2);
    //    out->push_back(tower);
    //    t.clear();
    //    
    //    tower = new Tower(t,3);
    //    out->push_back(tower);
    //    t.clear();
    //    
    //    t.push_back(3); 
    //    tower = new Tower(t,4);
    //    out->push_back(tower);




    for (int i = 1; i <= this->numberOfTowers; i++) {
        t.clear();
        for (int j = this->numberOfDisks - 1; j >= 0; j--) {
            if (pole[j] == i) t.push_back(j + 1);

        }
        //        if (t.size()==0) return false;
        tower = new Tower(t, i);
        out->push_back(tower);
    }
    return true;
}

void Game::doMove(Move* m) {
    //Z dat v promenne m si nactu spravne veze, z puvodni odeberu disk, na cilovou pridam
    Tower* from = this->towers.at(m->from - 1);
    Tower* to = this->towers.at(m->to - 1);
    if (from->lastDisc() != m->Disc) return;
    from->remove();
    to->add(m->Disc);
//    this->done++;
}

void Game::undoMove(Move* m) {
    //Z dat v promenne m si nactu veze v obracenem poradi, takze vlastne z cilove odebiram a na vychozi pridavam
    Tower* from = this->towers.at(m->to - 1);
    Tower* to = this->towers.at(m->from - 1);
    if (from->lastDisc() != m->Disc) return;
    from->remove();
    to->add(m->Disc);
}

vector<Move> Game::getMoves(int x) {
    vector<Move> out;
    Move move;
    //Vyberu s kazde veze posledni disk a zkousim u vsech vezi jestli je mozny ho tam dat
    move.turn = x;
    for (int i = 0; i<this->numberOfTowers; i++) {
//        if (this->towers.at(i)->size == 0 ) continue;
        move.Disc = this->towers.at(i)->lastDisc();
        move.from = towers.at(i)->number;
        for (int j = 0; j<this->numberOfTowers; j++) {
            if (i == j) continue;
            if (this->towers.at(j)->lastDisc() > move.Disc) {
                move.to = this->towers.at(j)->number;
                out.push_back(move);
            }
        }
    }

    return out;
}

bool Game::finished() {
    //Cilove veze se zeptam jestli je kompletni, kdyz je tak pro kontrolu se jeste zbylych ptam jestli jsou prazdne
    if (this->towers.at(this->targetTower - 1)->isComplete(this->numberOfDisks)) {
        for (int i = 0; i<this->numberOfTowers; i++) {
            if (i + 1 == this->targetTower) continue;
            if (!this->towers.at(i)->isEmpty() == true) return false;
        }
        return true;
    } else return false;
}

bool Game::backwardMove(Move* a, Move* b) {
    if (a->Disc == b->Disc) {
        if (a->from == b->to && a->to == b->from) return true;
        if (b->from == a->to && b->to == a->from) return true;
    }
    return false;
}

void Game::checkIntegrity() {
    int* board = new int[12];
    for (int i = 0; i<this->numberOfDisks; i++) board[i] = 0;
    for (int i = 0; i<this->numberOfTowers; i++) {
        this->towers.at(i)->decompose(board);
    }
    for (int i = 0; i<this->numberOfDisks; i++) {
        cout << board[i] << " ";
    }
    cout << endl;


    for (int i = 0; i<this->numberOfDisks; i++) {
        if (board[i] == 0) {
            cout << "!!!!!!!!!!!!!!!!!!FUUUUUUUU!!!!!!!!!!!!!1" << endl;
        }
    }
}

vector<Move> Game::play(ofstream& log, int &my_rank) {
    bool working=false, finished = false, awaitResponse = false, init = false,inited=false;
    bool token_override = false;
    int donor = 0,p, initialized=0;
    Stack2* stack = new Stack2(1);
    vector<Move> allMoves,solution,dataMove;
    
    Move move, prev;
    int condition = 0, max, lowerBound, citac = 0, flag, data;
    long count = 0;
    int token[2] = {0,0};
    MPI_Status status;
    Comm comm;

    
    prev.Disc = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &p);
    lowerBound = this->getLowerBound();
    max = this->getUpperBound();
#ifdef DEBUG
    cout << "Dolni mez : " << lowerBound << endl;
    cout << "Horni mez : " << max << endl;
#endif    
//        max=7;
//    max++;
//    max = 16;
    if (my_rank == 0) {
        token[0] = TOKEN_WHITE;
        token[1] = -1;
        working = true;
        inited = true;
        if (p>1) init = true;
    } else {
        working = false;
        inited = false;
    }

    do {

        citac++;
        if ((citac % CHECK_MSG_AMOUNT) == 0) {
            MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
            if (flag) {
                flag = 0;
                //prisla zprava, je treba ji obslouzit
                //v promenne status je tag (status.MPI_TAG), cislo odesilatele (status.MPI_SOURCE)
                //a pripadne cislo chyby (status.MPI_ERROR)
                switch (status.MPI_TAG) {
                    case MSG_WORK_REQUEST: // zadost o praci, prijmout a dopovedet
                        // zaslat rozdeleny zasobnik a nebo odmitnuti MSG_WORK_NOWORK     
                        MPI_Recv(&data, 1, MPI_INT, status.MPI_SOURCE, MSG_WORK_REQUEST, MPI_COMM_WORLD, &status);
                        
#ifdef DEBUG
                        log << "WORK REQUEST RECIEVED FROM " << status.MPI_SOURCE << endl;  
                        log.flush();
#endif
                                                                       
                        if (!stack->isEmpty()) {
                            if (init) {
                            initialized++;
#ifdef DEBUG
                        log << initialized << " FROM " << p-1 << " CPUs INITIALIZED"<< endl;
                        log.flush();
#endif
                        }
#ifdef DEBUG
                        log << "SENDING WORK SENT TO " << status.MPI_SOURCE << endl;
                        log.flush();
#endif
                        MPI_Send(&my_rank, 1, MPI_INT, status.MPI_SOURCE, MSG_WORK_SENT, MPI_COMM_WORLD);
                        dataMove.clear();
                        if (init) {
#ifdef DEBUG
                        log << "        SENDING ONLY TOP BECAUSE INIT = "<<init << endl;
                        log.flush();
#endif
                        dataMove.push_back(*stack->Top());
                        stack->Pop();
                        }
                        else {
#ifdef DEBUG
                            log << "        SPLITTING STACK BECAUSE INIT = "<<init << endl;
                            log.flush();
#endif
                            stack->split(&dataMove,log); 
                            }
#ifdef DEBUG
                        log << "        SENDING WORK" << endl;
                        log.flush();
#endif                                                         
                            comm.send(&dataMove,status.MPI_SOURCE,log);
                          
                            int pokus = dataMove.at(dataMove.size()-1).turn;
                            dataMove.clear();
                            
                            for(int i=0;i<pokus-1;i++) {
                                dataMove.push_back(actualMoves.at(i));
                            }
                            
#ifdef DEBUG
                        log << "        SENDING ACTUALMOVES" << endl;
                        log.flush();
#endif                            
                            
                            comm.send(&dataMove,status.MPI_SOURCE,log);
                            if (status.MPI_SOURCE < my_rank) token_override = true;
                            
                        }
                        else {
#ifdef DEBUG
                        log << "SENDING NOWORK TO " << status.MPI_SOURCE << endl;  
                        log.flush();
#endif
                            MPI_Send(&my_rank, 1, MPI_INT, status.MPI_SOURCE, MSG_WORK_NOWORK, MPI_COMM_WORLD);
                        if (init)     {
#ifdef DEBUG
                        log << "STILL NOT INITIALIZED SO GOING TO WORK " << endl;  
                        log.flush();
#endif
                        working = true;
                        }

                        }
                        if (init && initialized==p-1) {
                            init = false;
                            working = true;
#ifdef DEBUG
                        log << "ALL CPUs INITIALIZED" << endl;  
                        log << "STACK : " << endl;
                        stack->printStack(log);
                        log.flush();
#endif
                        }
                        
                        break;
                    case MSG_WORK_SENT: // prisel rozdeleny zasobnik, prijmout
                        // deserializovat a spustit vypocet
                        MPI_Recv(&data, 1, MPI_INT, status.MPI_SOURCE, MSG_WORK_SENT, MPI_COMM_WORLD, &status);
#ifdef DEBUG
                        log << "WORK SENT RECIEVED FROM " << status.MPI_SOURCE << endl;    
                        log << "        RECIEVING WORK" << endl;
                        log.flush();
#endif                         
                        
                        //PRIJMAM PRACI (CAST ZASOBNIKU DARCE)
                        dataMove.clear();
                        comm.recieve(&dataMove,status.MPI_SOURCE,log);
                        stack->del();
#ifdef DEBUG                        
                        log << "        STACK BEFORE : " << endl;
                        stack->printStack(log);
                        log.flush();
#endif                        
                        //AZ NA POSLEDNI SI TO DAM NA SVUJ ZASOBNIK
                        for(int i=0;i<dataMove.size()-1;i++) {
                            stack->Push(&dataMove.at(i));
                        }
#ifdef DEBUG                        
                        log << "        STACK NOW LOOKS : " << endl;
                        stack->printStack(log);
                        log.flush();
#endif
                        
                        //PRIJMU PREDESLE TAHY
                        actualMoves.clear();
                        comm.recieve(&actualMoves,status.MPI_SOURCE,log);
#ifdef DEBUG
                        log << "        RESETING GAME AND DOING RECIEVED MOVES" << endl;   
                        log.flush();
#endif                        
                        //RESETUJU STAV HRY DO POCATENIHO STAVU
                        this->reset();
#ifdef DEBUG
                        log << "        ACTUAL MOVES : " << endl;   
#endif
                        //UDELAM PREDESLE TAHY CO JSEM DOSTAL OD DARCE
                        for(int i=0;i<actualMoves.size();i++) {
                            this->doMove(&actualMoves.at(i));
#ifdef DEBUG
                        
                        this->printMove(log,&actualMoves.at(i));
                        log.flush();
#endif
                        }
                        
                        
                        
#ifdef DEBUG
                        log << "        DOING MOVE" << endl;
                        this->printMove(log,&dataMove.back());
                        log.flush();
#endif                        
                        //UDELAM ROVNOU POSLEDNI TAH Z PRIJATE PRACE A STARTUJI VYPOCET
                        actualMoves.push_back(dataMove.back());
                        
//                        count++;
                        
                        this->doMove(&dataMove.back());
                        prev = dataMove.back();
                        working = true;
#ifdef DEBUG    
                                              
//                        log << "        GAME NOW LOOKS : " << endl;
//                        this->printGame(log);
//                        log.flush();
#endif                        
                        //UZ NECEKAM NA ODPOVED A POKUD JSEM NEBYL INICIALIZOVAN TAK UZ JSEM
                        awaitResponse = false;
                        if (!inited) inited=true;
                        
                        break;
                    case MSG_WORK_NOWORK: // odmitnuti zadosti o praci
                        // zkusit jiny proces
                        // a nebo se prepnout do pasivniho stavu a cekat na token
                        MPI_Recv(&data, 1, MPI_INT, status.MPI_SOURCE, MSG_WORK_NOWORK, MPI_COMM_WORLD, &status);
#ifdef DEBUG
                        log << "RECIEVED NOWORK FROM " << status.MPI_SOURCE << endl;
                        log.flush();
#endif
                        //JESTLI MI ODPOVEDEL ZE NEMA PRACI CPU CO PREDTIM V TOKENU RIKAL ZE MA NULUJU TOKEN
                        if (donor == token[1]) token[1] = -1;
                        
                        //JESTLI UZ PROBEHLA INICIALIZACE VYBIRAM NAHODNE DARCE
                        if (!inited) {
                            srand(time(NULL)+my_rank);
                            do {
                                donor = rand() % p;
                            }
                            while( donor == my_rank);
                        }
#ifdef DEBUG
                        log << "        NEXT DONOR SET TO " << donor << " AWAITRESPONSE = FALSE AND IM GOING TO SLEEP"<<endl;                        
                        log.flush();
#endif                        
                        awaitResponse = false;
                        sleep(1);
                        break;
                    case MSG_TOKEN: 
                        MPI_Recv(&token, 2, MPI_INT, status.MPI_SOURCE, MSG_TOKEN, MPI_COMM_WORLD, &status);
#ifdef DEBUG    
                        log << "RECIEVED TOKEN "<< token[0] << " FROM " << status.MPI_SOURCE << endl;
                        log.flush();
#endif  
                        //SEM "VUDCE" A PRISEL BILY TOKEN, TAKZE KONEC
                        if (my_rank==0 ) {
                            if (token[0] == TOKEN_WHITE) {
#ifdef DEBUG    
                        log << "                IM LEADER SO WORK IS FINISHED " << endl;
                        log.flush();
#endif                                
                                finished = true;
                            }
                            else {
                                //PRISEL CERNY TOKEN, NASTAVUJU PODLE NEJ DARCE
#ifdef DEBUG    
                        log << "                IM LEADER SO AWAITRESPONSE = FALSE" << endl;
                        log.flush();
#endif
                        donor = token[1]; 
                        awaitResponse = false;
                            }
                        } else {
                            //NEJSEM "VUDCE"                            
                            if (working || token[0]==TOKEN_BLACK || token_override) {
                                //PRACUJU NEBO TOKEN UZ BYL CERNY POSILAM DAL CERNY
#ifdef DEBUG    
                                if (working)
                                        log << "        SETTING TOKEN BLACK BECAUSE IM WORKING"<< endl;
                                if (token[0]==TOKEN_BLACK)
                                        log << "        SETTING TOKEN BLACK BECAUSE WAS BLACK ALREADY"<< endl;
                                if (token_override)
                                        log << "        SETTING TOKEN BLACK BECAUSE OF OVERRIDE"<< endl;
                                log.flush();
#endif                                
                                token[0] = TOKEN_BLACK;
                                if (working) token[1] = my_rank;
                                token_override = false;
                            }
                            else {
                                token[0] = TOKEN_WHITE;
#ifdef DEBUG    
                            log << "    SETTING TOKEN WHITE"<< endl;
                            log.flush();
#endif                                 
                            }
#ifdef DEBUG    
                            log << "SENDING TOKEN "<< token[0] << " TO " << ( my_rank+1 ) % p << endl;
                            log.flush();
#endif
                            MPI_Send(&token, 2, MPI_INT, ( my_rank+1 ) % p, MSG_TOKEN, MPI_COMM_WORLD);
                        }
                        break;
                    case MSG_FINISH: //konec vypoctu - proces 0 pomoci tokenu zjistil, ze jiz nikdo nema praci
                        //a rozeslal zpravu ukoncujici vypocet
                        //mam-li reseni, odeslu procesu 0
                        //nasledne ukoncim spoji cinnost
                        //jestlize se meri cas, nezapomen zavolat koncovou barieru MPI_Barrier (MPI_COMM_WORLD)
                        MPI_Recv(&data, 1, MPI_INT, status.MPI_SOURCE, MSG_FINISH, MPI_COMM_WORLD, &status);
#ifdef DEBUG    
                            log << "I RECIEVED FINISH FROM "<< status.MPI_SOURCE << endl;
                            log.flush();
#endif                        
                        finished = true;
                        working = false;
//                        if (solution.size() != 0) {
#ifdef DEBUG
                                log << "        SENDING MY SOLUTION TO 0" << endl;
//                                log << solution.size() << endl;
//                                for (int i = 0; i < solution.size(); i++) {
//                                    this->printMove(log,&solution.at(i));
//                                }
                                log.flush();
#endif                    
                                //POSILAM SVOJE RESENI "VUDCI"
                                MPI_Send(&init, 3, MPI_INT, 0, MSG_SOLUTION, MPI_COMM_WORLD);
                                
                                comm.send(&solution,0,log);
//                        }
                        //UKLIDIM A KONEC
//                                cout << my_rank << ".CPU COUNT " << count << " DONE " << this->getDone() << endl;
                                
                                MPI_Barrier(MPI_COMM_WORLD);
                                MPI_Finalize();
#ifdef DEBUG
                        log << "        ENDING" << endl;
                        if (stack->isEmpty()) {
                            log << "            STACK EMPTY" << endl;
                        }
                        else log << "           STACK NOT EMPTY!!" << endl;
                        log.flush();

                        log.close();
#endif                                          
                        exit(0);
                        break;
                        case MSG_BOUND_UPDATE:
                            //PRICHOD UPDATU HORNI MEZE
                            MPI_Recv(&max, 1, MPI_INT, status.MPI_SOURCE, MSG_BOUND_UPDATE, MPI_COMM_WORLD, &status);
#ifdef DEBUG                
                        log << "RECIEVED BOUND UPDATE TO "<< max << " FROM " << status.MPI_SOURCE <<endl;
                        log.flush();
#endif 
                            break;
                    default: log << ("neznamy typ zpravy");
                        break;
                }
            }
        }


        if (working) {
            //JESTLI TEPRVE INICIALIZUJU CHCI UDELAT JENOM JEDEN KROK, TAKZE PRESTAVAM PRACOVAT
            if (init) {
#ifdef DEBUG                
                log << "NOT INITIALIZED IM NOT WORKING"<< endl;
                log.flush();
#endif                
                working = false;
                
            }
//            cout << "IM WORKING"<<endl;
            allMoves.clear();
            allMoves = this->getMoves(actualMoves.size() + 1);
//            cout << "POSSIBLE MOVES " << endl;
//                    for (int i = 0; i < allMoves.size(); i++) {
//                        this->printMove(&allMoves.at(i));
//                    }
            this->removeUnusefulMoves(&prev, &allMoves);

                    

            //Kdyz neni mozny dalsi tah, vracim se o krok zpatky a jako dalsi tah beru vrchol zasobniku
            if (allMoves.size() == 0 || actualMoves.size() >= max) {
#ifdef DEBUG                
//                this->printGame(log);    
//                log << "GOING BACK ACTUALMOVES SIZE = "
//                            << actualMoves.size()                        
//                            << " MAX = " << max 
//                            << " ALLMOVES SIZE = "
//                            << allMoves.size() << endl;
//    //                log << (allMoves.size() == 0) << " " << (actualMoves.size() >= max) << " "<< (allMoves.size() == 0 || actualMoves.size() >= max)<<endl;

//                    log.flush();
#endif
                if (stack->isEmpty()) {
#ifdef DEBUG                                
//                    log << "    BUT I HAVE EMPTY STACK SO IM STOPPING WORK " << endl;
//                    log << "        STACK NOW LOOKS : " << endl;
//                        stack->printStack(log);
//                        log << "        END OF STACK" << endl;
//                        log.flush();

#endif                      
                    working = false;
                }
                else {
                
                
                
                    this->undoMove(&actualMoves.back());
                    actualMoves.pop_back();
                    move = *stack->Top();
                    stack->Pop();

#ifdef DEBUG                                
//                    log << "GOING BACK TO MOVE " << endl;
//                    this->printMove(log,&move);
#endif                

                    //Kontrola jestli se nemusim vracet o vic tahu
                    if (move.turn != actualMoves.size() + 1) {

                        do {
#ifdef DEBUG                             
//                            log << "        UNDOING MOVE" << endl;
//                            this->printMove(log,&actualMoves.back());
#endif

                            this->undoMove(&actualMoves.back());
                            actualMoves.pop_back();
                        } while (move.turn != actualMoves.size() + 1);
                    }
                    allMoves.clear();
                }
            } else {
                // Stridave vybiram prvni nebo posledni tah

                if (condition == 0) {
                    move = allMoves.front();
                    allMoves.erase(allMoves.begin());
//                                    condition = 1;
                } else {
                    move = allMoves.back();                    
                    allMoves.pop_back();
                    condition = 0;
                }
            }
            //Zbyle tahy dam na zasobnik
            for (int i = 0; i < allMoves.size(); i++) {
                stack->Push(&allMoves.at(i));
            }
//                    cout << (stack->isEmpty()) << endl;

//                    cout << "Doing move : ";
//                    this->printMove(&move);
#ifdef DEBUG
//                        log << "DOING MOVE "  << endl;                         
//                        this->printMove(log,&move);
//                        log.flush();
#endif
//            if (working) {
//            count++;
            this->doMove(&move);
            //        this->printGame();
            actualMoves.push_back(move);
//            }
            
#ifdef DEBUG
//            if (working)
//            for(int i=0;i<actualMoves.size();i++) {
//                if (actualMoves.at(i).turn != i+1) {
//                    cout << "MADE WRONG MOVE" << endl;
//                    log << "!!!!MAKING WRONG MOVE!!!!" << endl;
//                    for(int j=0;j<actualMoves.size();j++) {
//                        this->printMove(log,&actualMoves.at(j));
//                    }
//                    break;
//                }
//            }
#endif            
            
            if (this->finished() == true) {
                if (actualMoves.size()<max) {
                    max = actualMoves.size();      
                    for(int i=0;i<p;i++) {
                        if (i==my_rank) continue;
#ifdef DEBUG
                            log << "SENDING BOUND UPDATE "<< max << " TO "<<i<<endl;
                            log.flush();
#endif    
                        MPI_Send(&max, 1, MPI_INT, i, MSG_BOUND_UPDATE, MPI_COMM_WORLD);
                    }
                }
//                            log << "FOUND SOLUTION SIZE " << actualMoves.size() << endl;
//                            cout << "FOUND SOLUTION SIZE " << actualMoves.size() << endl;
                          
                
                if (solution.size() > actualMoves.size() || solution.size() == 0) {
                    solution.clear();
                    for (int i = 0; i < actualMoves.size(); i++) {
                        solution.push_back(actualMoves.at(i));
                    }
                    if (solution.size() == lowerBound) {
#ifdef DEBUG
                            log << "I FOUND BEST SOLUTION FINISHED = TRUE WORKING = FALSE" <<endl;
                            log.flush();
#endif                        
//                        finished = true;
                                            token[0] = TOKEN_WHITE;
#ifdef DEBUG
                log << "        SENDING TOKEN " << token[0] << " TO " << 0 << endl;
                log.flush();
#endif
                MPI_Send(&token, 2, MPI_INT, 0, MSG_TOKEN, MPI_COMM_WORLD);
                        working = false;
//                        break;
                    }
                }
            }
//            //Zbyle tahy dam na zasobnik
//            for (int i = 0; i < allMoves.size(); i++) {
//                stack->Push(&allMoves.at(i));
//            }

            prev = move;
        }//working==true
        else {
#ifdef DEBUG
            //            log << "IM NOT WORKING" << endl;
            //            log.flush();
#endif

            if (my_rank == 0 && !awaitResponse && !init && token[1] == -1) {
                token[0] = TOKEN_WHITE;
                token[1] = -1;
#ifdef DEBUG
                log << "        SENDING TOKEN " << token[0] << " TO " << (my_rank + 1) % p << endl;
                log.flush();
#endif
                MPI_Send(&token, 2, MPI_INT, (my_rank + 1) % p, MSG_TOKEN, MPI_COMM_WORLD);
                //                finished = true;
                awaitResponse = true;
            }

            if (!awaitResponse && !finished && !init) {
#ifdef DEBUG
                log << "        SENDING WORK REQUEST TO " << donor << endl;
                log.flush();
#endif
                MPI_Send(&my_rank, 1, MPI_INT, donor, MSG_WORK_REQUEST, MPI_COMM_WORLD);
                awaitResponse = true;
#ifdef DEBUG
                log << "        REQUEST SENT" << endl;
                log.flush();
#endif
            } else {
#ifdef DEBUG
                //                log << "        AWAITRESPONSE = " << awaitResponse << " INIT = " << init << endl;
                //                log.flush();
#endif                
            }
        }

        if (working && stack->isEmpty() &&
                actualMoves.size() >= max) {
            //            log << "STACK EMPTY "<< stack->isEmpty() << endl;
            //            log << " this->getMoves(actualMoves.size()+1).size() == 0 " << (this->getMoves(actualMoves.size()+1).size() == 0) << endl;
            //            log << " actualMoves.size() >= max " << (actualMoves.size() >= max) << endl;
            //            log << "STOPPING WORK"<<endl;
#ifdef DEBUG
            log << "    I HAVE EMPTY STACK AND ACTUALMOVES >= MAX SO IM NOT WORKING" << endl;
            log.flush();
            log << "IM NOT LYING CHECK MY STACK" << endl;
            stack->printStack(log);
            log << "END OF STACK" << endl;

#endif
            working = false;
        }
        //        else working = true;

        //            } while ((!stack->isEmpty() ||
        //                    (this->getMoves(actualMoves.size() + 1).size() != 0 && actualMoves.size() < max)) &&
        //                    !finished);

    } while (!finished);

#ifdef DEBUG    
    if (stack->isEmpty()) {
        log << "STACK EMPTY" << endl;
        log.flush();
    } else {
        log << "STACK NOT EMPTY" << endl;
        log.flush();
    }
#endif

    if (finished) {
        //        if (my_rank == 0) {
        for (int i = 1; i < p; i++) {
#ifdef DEBUG
            log << "SENDING FINISH TO " << i << endl;
            log.flush();
#endif
            MPI_Send(&my_rank, 1, MPI_INT, i, MSG_FINISH, MPI_COMM_WORLD);
        }
        //        }
        //        else {
        //            
        //        }
    }
    //    cout << my_rank << ".CPU COUNT " << count << " DONE " << this->getDone() << endl;
    return solution;
}


//odebere ze seznamu tahu ty nesmyslny (zpetny a tahy se stejnym diskem)

void Game::removeUnusefulMoves(Move* clue, vector<Move>* moves) {



    for (int i = 0; i < moves->size(); i++) {
        if (
                //                this->backwardMove(clue, &moves->at(i)) == true ||
                moves->at(i).Disc == clue->Disc) {
            //            cout << "REMOVING MOVE " ;
            //                    this->printMove(&moves->at(i));
            //            cout << endl;
            moves->erase(moves->begin() + i);
            i--;


        }
    }
    //    cout << "POSSIBLE MOVES NOW" << endl;
    //                    for (int i = 0; i < moves->size(); i++) {
    //                        this->printMove(&moves->at(i));
    //                    }

}

//vypocet horni meze

int Game::getUpperBound() {
    switch (this->numberOfTowers) {
        case 4:
            switch (this->numberOfDisks) {
                case 12:
                    return 30;
                case 13:
                    return 36;
                case 14:
                    return 42;
            }
            break;
        case 5:
            switch (this->numberOfDisks) {
                case 12:
                    return 40;
                case 13:
                    return 48;
                case 14:
                    return 56;
            }
            break;
    }
    int a, b, c, d;
    a = this->numberOfDisks / (this->numberOfTowers - 2);
    b = pow(2, a);
    c = (2 * this->numberOfTowers) - 5;
    d = (b - 1) * c;
    //    cout << "A = " << a << endl;
    //    cout << "B = " << b << endl;
    //    cout << "C = " << c << endl;
    //    cout << "D = " << d << endl;
    return (int) ((pow(2, this->numberOfDisks / (this->numberOfTowers - 2)) - 1)*((2 * this->numberOfTowers) - 5));
}

//vypocet dolni meze

int Game::getLowerBound() {
    int ret = 0;
    //    int* desk = new int[this->numberOfDisks];
    int* desk = (int*) calloc(this->numberOfDisks, sizeof (int));
    for (int i = 0; i<this->numberOfTowers; i++) {
        this->towers.at(i)->decompose(desk);
    }
    for (int j = this->numberOfDisks - 1; j >= 0; j--) {
        if (desk[j] != this->targetTower) {
            //                cout << "Disc " << j+1 << "  +1" << endl;
            ret++;
        } else {
            if (j<this->numberOfDisks - 1) {
                if (desk[j - 1] != this->targetTower) {
                    for (int i = j - 2; i >= 0; i--) {
                        if (desk[i] == desk[j - 1]) {
                            //                                cout << "Disc " << j+1 << "  +2" << endl;
                            ret += 2;
                            break;
                        }
                    }

                }
            }
        }
    }

    return ret;
}

//Vypis tahu

void Game::printMove(Move* m) {
    cout << m->Disc << "," << m->from << " -> " << m->to << " turn : " << m->turn << endl;
    if (m->from == m->to) {
        cout << "       !!!!!ERROR" << endl;
    }
}

void Game::printMove(ofstream& out, Move* m) {
    out << m->Disc << "," << m->from << " -> " << m->to << " turn : " << m->turn << endl;
    if (m->from == m->to) {
        out << "       !!!!!ERROR" << endl;
    }
}

int* Game::getDiscs() {
    return this->discs;
}

void Game::reset() {
    this->newGame(this->discs);
}

long Game::getDone() {
    return this->done;
}

